﻿// Copyright Microsoft

using System;
using System.Activities.Core.Presentation;
using System.Activities.Presentation;
using System.Activities.Presentation.Model;
using System.Activities.Presentation.Toolbox;
using System.Activities.Presentation.Validation;
using System.Activities.Presentation.View;
using System.Activities.Statements;
using System.Collections.Generic;
using System.IO;
using System.Windows.Controls;
using Microsoft.Samples.SqlServer.Activities;
using Microsoft.Samples.SqlServer.ExtensionMethods;
using System.Collections.ObjectModel;
using Microsoft.Samples.SqlServer.Activities.ActivityPublishers;
using Microsoft.Samples.SqlServer.Activities.Designers.ActivityPublishers;
using System.Activities.Presentation.Services;
using System.Activities;
using Microsoft.VisualBasic.Activities;
using System.Linq;
using Microsoft.Samples.SqlServer.Activities.Designers.OData;
using Microsoft.Samples.SqlServer.Activities.Designers;
using System.Data.SqlClient;
using System.Net;


//NEXT: Improve how to assign SqlConnectionStringBuilder. Currently, you need to assign SqlConnectionStringBuilder before loading the workflow
namespace Microsoft.Samples.SqlServer.Workflow.Designer
{
  public partial class WorkflowDesignerControl : UserControl
  {

    public string ClientName { get; set; }
    public SqlConnectionStringBuilder SqlConnectionStringBuilder { get; set; }

    #region Private Variables
    private CustomEditorService expressionEditorService;
    private WorkflowDesigner workflowDesigner;
    private ModelService modelService;
    private ValidationErrorService errorService;        
    private string currentWorkflowPath = string.Empty;
    private DesignerView designerView;       
    #endregion

    public WorkflowDesignerControl()
    {     
        InitializeComponent();
    }

    public string WorkflowPath
    {
        get
        {
            return currentWorkflowPath;
        }
    }


    #region Workflow Designer

    private void Init()
    {
        //Register designers for the standard activities
        DesignerMetadata metaData = new DesignerMetadata();
        metaData.Register();

        //Load custom expression editor
        this.expressionEditorService = new CustomEditorService();
        errorService = new ValidationErrorService(this.messageListBox);

        this.InitializeDesigner();
    }

    private void InitializeDesigner()
    {
        //Create a designer
        workflowDesigner = new WorkflowDesigner();
        workflowDesigner.ModelChanged += new EventHandler(workflowDesigner_ModelChanged);

        designerArea.Child = workflowDesigner.View;

        //Publish the instance of CustomEditorService.
        workflowDesigner.Context.Services.Publish<IExpressionEditorService>(this.expressionEditorService);

        //Error service
        workflowDesigner.Context.Services.Publish<IValidationErrorService>(errorService);

        if (this.SqlConnectionStringBuilder == null)
        {
            LoginForm loginForm = new LoginForm();
            loginForm.DataAvailable += new LoginForm.ActivityFormEventHandler(loginForm_DataAvailable);
            loginForm.ShowDialog();
        }
    }

    void loginForm_DataAvailable(object sender, ActivityFormEventArgs e)
    {
        if (e.NetworkCredential != null)
        {
            this.SqlConnectionStringBuilder = new SqlConnectionStringBuilder();
            this.SqlConnectionStringBuilder.UserID = (e.NetworkCredential as NetworkCredential).UserName;

            switch (e.ConnectionContext)
            {
                case ConnectionContext.IntegratedSecurity:
                    {
                        this.SqlConnectionStringBuilder.IntegratedSecurity = true;
                        break;
                    }
                case ConnectionContext.SqlAzure:
                    {
                        this.SqlConnectionStringBuilder.Encrypt = true;
                        this.SqlConnectionStringBuilder.TrustServerCertificate = false;
                        this.SqlConnectionStringBuilder.UserID = (e.NetworkCredential as NetworkCredential).UserName;
                        this.SqlConnectionStringBuilder.Password = (e.NetworkCredential as NetworkCredential).Password;
                        break;
                    }
            }
        }
    }

    private void SetDesignerConnection()
    {
        SelectDataTableDesigner selectDataTableDesigner;
        IEnumerable<ModelItem> activityCollection = modelService.Find(modelService.Root, typeof(Activity));

        IEnumerable<ModelItem> selectDataTableActivities = from mi in activityCollection
                                                           where mi.ItemType == typeof(Microsoft.Samples.SqlServer.Activities.SelectDataTableActivity)
                                                           select mi;

        foreach (ModelItem mi in selectDataTableActivities)
        {
            selectDataTableDesigner = workflowDesigner.Context.Services.GetService<ViewService>().GetView(mi) as SelectDataTableDesigner;
            selectDataTableDesigner.SqlConnectionStringBuilder = this.SqlConnectionStringBuilder;
        }


        SqlWhereDesigner whereActivityDesigner;
        IEnumerable<ModelItem> whereActivities = from mi in activityCollection
                                                 where mi.ItemType == typeof(Microsoft.Samples.SqlServer.Activities.SqlWhere)
                                                 select mi;

        foreach (ModelItem mi in whereActivities)
        {
            whereActivityDesigner = workflowDesigner.Context.Services.GetService<ViewService>().GetView(mi) as SqlWhereDesigner;
            whereActivityDesigner.SqlConnectionStringBuilder = this.SqlConnectionStringBuilder;
        }
    }
    private void AssignDelegates()
    {
        designerView = workflowDesigner.Context.Services.GetService<DesignerView>();
        modelService = workflowDesigner.Context.Services.GetService<ModelService>();

        //Handle Flow Diagram
        designerView.PreviewMouseDoubleClick += new System.Windows.Input.MouseButtonEventHandler(designerView_PreviewMouseDoubleClick);
    }

    /// <summary>
    /// New workflow
    /// </summary>
    /// <returns></returns>
    public WorkflowDesigner New()
    {
        this.Init();       
  
        Sequence newSequence = new Sequence();
        //Auto-set variables for this sample

        newSequence.Variables.Add(new Variable<IEnumerable<IEnumerable<EntityProperty>>>() { Name = "EntityProperties" });
        newSequence.Variables.Add(new Variable<ICredentials>() { Name = "Credentials" });
        newSequence.Variables.Add(new Variable<SqlConnectionStringBuilder>() { Name = "ConnectionStringBuilder" });

        try
        {
            workflowDesigner.Load(new ActivityBuilder { Implementation = newSequence });

            this.AssignDelegates();

            ModelTreeManager mtm = workflowDesigner.Context.Services.GetService<ModelTreeManager>();
            ModelItem ab = mtm.Root;
            ModelItemCollection argsAndProperties = ab.Properties["Properties"].Collection;
            argsAndProperties.Add(new DynamicActivityProperty
            {
                Name = "Handle",
                Type = typeof(InArgument<IntPtr>)
            });
           
            currentWorkflowPath  = string.Format(@"{0}\New Activity.xaml", Properties.Settings.Default.SamplePath);

            //NEXT: Change hard-coded style names
            this.SetStyleNames();

            this.SetDesignerConnection();
        }
        catch
        {
            //Handle exception in a production application
        }

        return workflowDesigner;
    }


    /// <summary>
    /// Load a workflow.
    /// </summary>
    /// <param name="path"></param>
    /// <returns></returns>
    public WorkflowDesigner Load(string path)
    {
        currentWorkflowPath = path;

        this.Init();

        try
        {
            workflowDesigner.Load(currentWorkflowPath);
            this.AssignDelegates();
            this.SetStyleNames();

            //NOTE: Must connect before load
            this.SetDesignerConnection();
        }
        catch
        {
            //Handle exception in a production application
        }

        return workflowDesigner;
    }

    /// <summary>
    /// Sample set style names from client. This is hard-coded for the sample.
    /// A production application would factor this code much differently
    /// </summary>
    private void SetStyleNames()
    {
        TablePartPublisherDesigner activityPublisher = null;
        IEnumerable<ModelItem> activityCollection = modelService.Find(modelService.Root, typeof(Activity));

        IEnumerable<ModelItem> tablePartPublishers = from mi in activityCollection
                                          where mi.ItemType == typeof(Microsoft.Samples.SqlServer.Activities.ActivityPublishers.TablePartPublisher)
                                          select mi;

        foreach (ModelItem mi in tablePartPublishers)
        {
            activityPublisher = workflowDesigner.Context.Services.GetService<ViewService>().GetView(mi) as TablePartPublisherDesigner;
            activityPublisher.StyleNames.Clear();

            switch (this.ClientName)
            {
                case "Word":
                    {
                        activityPublisher.StyleNames.Add("Light Shading - Accent 5");
                        activityPublisher.StyleNames.Add("Light Shading - Accent 2");
                        activityPublisher.StyleNames.Add("Medium List 2 - Accent 5");
                        activityPublisher.StyleNames.Add("Medium Grid 1 - Accent 4");
                        activityPublisher.StyleNames.Add("Colorful List");
                        break;
                    }
                default:
                    {
                        activityPublisher.StyleNames.Add("TableStyleMedium9");
                        activityPublisher.StyleNames.Add("TableStyleMedium10");
                        activityPublisher.StyleNames.Add("TableStyleMedium20");
                        activityPublisher.StyleNames.Add("TableStyleMedium24");
                        activityPublisher.StyleNames.Add("TableStyleMedium28");
                        break;
                    }
            }
    }
    }

    /// <summary>
    /// Auto save the workflow
    /// </summary>
    /// <param name="path"></param>
    /// <param name="markup"></param>
    public void SaveXamlFile(String path, String markup)
    {
      try
      {
        using (FileStream stream = new FileStream(path, FileMode.Create))
        {
          using (StreamWriter writer = new StreamWriter(stream))
          {
            writer.Write(markup);
          }
        }
      }
      catch (Exception ex)
      {
        //Production application would handle catch
      }
    }
    #endregion

    #region Event Delegates

    /// <summary>
    /// FlowDiagram Activity
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    void designerView_PreviewMouseDoubleClick(object sender, System.Windows.Input.MouseButtonEventArgs e)
    {
        this.SetDesignerConnection();

        if ((sender as DesignerView).FocusedViewElement != null)
        {
          ModelItem mi = (sender as DesignerView).FocusedViewElement.ModelItem;
          if (mi.ItemType == typeof(FlowDecision))
          {       
            ConditionForm conditionForm = new ConditionForm();

            // Assume Flowchart is in Sequence    
            ModelItem sequence = mi.GetParent(typeof(Sequence));
            if (sequence != null)
            {
              conditionForm.Variables = sequence.Properties["Variables"].ComputedValue as Collection<System.Activities.Variable>;
            }

            //Set the condition forms ModelItem
            conditionForm.ConditionModelItem = mi;
            conditionForm.ShowDialog();
          }
      }
    }

    /// <summary>
    /// Update when model changes
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    void workflowDesigner_ModelChanged(object sender, EventArgs e)
    {
        workflowDesigner.Flush();
        this.SaveXamlFile(currentWorkflowPath, workflowDesigner.Text);

        designerView.UpdateLayout();

        //Set StyleNames and designer connection
        this.SetStyleNames();

        //TODO: Loosing ContextMenu when Open or Collapse?
        //TODO: Also, Where looses SQLConnection?
        this.SetDesignerConnection();
    }

    #endregion
  }

}
